package com.symaster.common.geom;

import cn.hutool.core.util.ObjectUtil;
import com.symaster.common.geom.entity.Node;
import com.symaster.common.geom.util.PointUtil;
import com.symaster.common.swing.util.SwingUtil;
import com.symaster.common.swing.util.ThreadPoolUtil;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.image.BufferedImage;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

/**
 * @author yinmiao
 * @version 2022-07-13 14:16
 */
public class PolygonComponent<T extends Node> extends JComponent {
    private final String id;
    private final T p;
    private java.awt.Polygon awtPolygon;
    private java.awt.Polygon awtBorderPolygon;
    private MyImage usualImage;
    private MyImage mouseSuspensionImage;
    private MyImage selectImage;
    private MyImage selectMouseSuspensionImageImage;
    private MyImage drawImage;
    private int w;
    private int h;
    private boolean mouseEntered = false;
    private boolean mouseSuspension = false;
    private boolean select = false;
    private double angle = 0.0D;
    private Set<SelectListener> selectListeners;
    private Point lastPoint;

    public PolygonComponent(T p) {
        this.p = ObjectUtil.clone(p);
        this.p.move(0, 0);
        this.id = UUID.randomUUID().toString();
        update(p);
    }

    private void update(T p) {
        double scaleX = PointUtil.getScale(this.p.getWidth(), this.p.getWidth() - 1.5);
        double scaleY = PointUtil.getScale(this.p.getHeight(), this.p.getHeight() - 1.5);

        this.awtBorderPolygon = PointUtil.scaleTransform(this.p.getPolygon(), scaleX, scaleY).toAwtPolygon();
        this.awtPolygon = this.p.getPolygon().toAwtPolygon();
        this.w = (int) p.getWidth();
        this.h = (int) p.getHeight();

        this.setSize(new Dimension(w, h));
        this.setMaximumSize(new Dimension(w, h));
        this.setMinimumSize(new Dimension(w, h));
        this.setPreferredSize(new Dimension(w, h));
        this.setBackground(new Color(0, 0, 0, 0));
        this.usualImage = createUsualImage();
        this.mouseSuspensionImage = createMouseSuspensionImage();
        this.selectImage = createSelectImage();
        this.selectMouseSuspensionImageImage = createSelectMouseSuspensionImageImage();
        SwingUtilities.invokeLater(this::repaint);
    }

    public void addSelectListener(SelectListener listener) {
        if (selectListeners == null) {
            selectListeners = new HashSet<>();
        }
        selectListeners.add(listener);
    }

    public void removeSelectListener(SelectListener listener) {
        if (selectListeners == null) {
            return;
        }
        selectListeners.add(listener);
    }

    public T getSrc() {
        return p;
    }

    public boolean include(Point point) {
        Point location = getLocation();
        int x = point.x - location.x;
        int y = point.y - location.y;
        return p.getPolygon().contains(x, y);
    }

    public void rotate(double angle) {
        if (this.angle != angle) {
            resetAngle(angle);
            p.move(0, 0);
            update(p);
            this.angle = angle;
        }
        SwingUtilities.invokeLater(this::repaint);
    }

    private void resetAngle(double angle) {
        if (this.angle != 0.0D) {
            p.rotateTransform(-this.angle);
        }
        p.rotateTransform(angle);
    }

    private MyImage createSelectMouseSuspensionImageImage() {
        MyImage myImage = createMyImage();
        displayPolygon(myImage, createSelectColorConfig());
        return myImage;
    }

    private MyImage createSelectImage() {
        MyImage myImage = createMyImage();
        displayPolygon(myImage, createSelectColorConfig());
        return myImage;
    }

    private MyImage createMouseSuspensionImage() {
        MyImage myImage = createMyImage();
        displayPolygon(myImage, createSuspensionColorConfig());
        return myImage;
    }

    private MyImage createUsualImage() {
        MyImage myImage = createMyImage();
        displayPolygon(myImage, createDefaultColorConfig());
        return myImage;
    }

    public String getId() {
        return id;
    }

    public Point getLastPoint() {
        return lastPoint;
    }

    public void setLastPoint(Point lastPoint) {
        this.lastPoint = lastPoint;
    }

    private void initMouseListener() {
        addMouseListener(new MouseAdapter() {

            @Override
            public void mouseEntered(MouseEvent e) {
                super.mouseEntered(e);
                mouseEntered = true;
            }

            @Override
            public void mouseExited(MouseEvent e) {
                super.mouseExited(e);
                mouseEntered = false;
                mouseSuspensionEnd();
            }
        });
        addMouseMotionListener(new MouseAdapter() {
            @Override
            public void mouseWheelMoved(MouseWheelEvent e) {
                System.out.println("mouseWheelMoved");
                super.mouseWheelMoved(e);
            }

            @Override
            public void mouseMoved(MouseEvent e) {
                super.mouseMoved(e);
                if (mouseEntered) mouseSuspensionStart();
            }
        });
    }

    public boolean isSelect() {
        return select;
    }

    public void mouseSuspensionStart() {
        if (mouseSuspension) {
            return;
        }
        mouseSuspension = true;
        if (select) {
            drawImage = selectMouseSuspensionImageImage;
            return;
        }
        drawImage = mouseSuspensionImage;
        repaint();
    }

    public void mouseSuspensionEnd() {
        if (!mouseSuspension) {
            return;
        }
        mouseSuspension = false;
        if (select) {
            drawImage = selectImage;
            return;
        }
        drawImage = usualImage;
        repaint();
    }

    public void selectPolygon() {
        if (select) {
            return;
        }
        select = true;
        drawImage = selectImage;
        selectListener();
        repaint();
    }

    public void unselectPolygon() {
        if (!select) {
            return;
        }
        select = false;
        drawImage = usualImage;
        unselectListener();
        repaint();
    }

    private void selectListener() {
        if (selectListeners == null || selectListeners.size() <= 0) {
            return;
        }
        selectListeners.forEach(e -> ThreadPoolUtil.execute(e::select));
    }

    private void unselectListener() {
        if (selectListeners == null || selectListeners.size() <= 0) {
            return;
        }
        selectListeners.forEach(e -> ThreadPoolUtil.execute(e::unselect));
    }

    public int getW() {
        return w;
    }

    public int getH() {
        return h;
    }

    public void displayPolygon(MyImage image, ColorConfig colorConfig) {
        image.graphics.setColor(colorConfig.getPolygonBackgroundColor());
        image.graphics.fillPolygon(awtPolygon);
        image.graphics.setColor(colorConfig.getPolygonBorderColor());
        image.graphics.drawPolygon(awtBorderPolygon);
    }

    private ColorConfig createDefaultColorConfig() {
        ColorConfig colorConfig = new ColorConfig();
        colorConfig.setPolygonBackgroundColor(new Color(60, 60, 60));
        colorConfig.setPolygonBorderColor(new Color(0, 0, 0));
        return colorConfig;
    }

    private ColorConfig createSuspensionColorConfig() {
        ColorConfig colorConfig = new ColorConfig();
        colorConfig.setPolygonBackgroundColor(new Color(80, 80, 80));
        colorConfig.setPolygonBorderColor(new Color(0, 0, 0));
        return colorConfig;
    }

    private ColorConfig createSelectColorConfig() {
        ColorConfig colorConfig = new ColorConfig();
        colorConfig.setPolygonBackgroundColor(new Color(105, 200, 218));
        colorConfig.setPolygonBorderColor(new Color(69, 100, 225));
        return colorConfig;
    }

    @Override
    public void paint(Graphics g) {

        super.paint(g);

        if (drawImage == null) {
            drawImage = usualImage;
        }

        g.drawImage(drawImage.image, 0, 0, null);
    }

    private MyImage createMyImage() {
        SwingUtil.CanvasImage canvasImage = SwingUtil.createCanvasImage(this.w, this.h, BufferedImage.TYPE_4BYTE_ABGR);

        MyImage myImage = new MyImage();
        myImage.image = canvasImage.getImage();
        myImage.graphics = canvasImage.getGraphics();
        return myImage;
    }

    public static class ColorConfig {
        private Color polygonBackgroundColor;
        private Color polygonBorderColor;

        public Color getPolygonBackgroundColor() {
            return polygonBackgroundColor;
        }

        public void setPolygonBackgroundColor(Color polygonBackgroundColor) {
            this.polygonBackgroundColor = polygonBackgroundColor;
        }

        public Color getPolygonBorderColor() {
            return polygonBorderColor;
        }

        public void setPolygonBorderColor(Color polygonBorderColor) {
            this.polygonBorderColor = polygonBorderColor;
        }
    }

    public static class MyImage {
        Image image;
        Graphics graphics;
    }

    public interface SelectListener {
        void select();

        void unselect();
    }
}
